
package traffic;


import java.util.*;

public class CarsControl 
{
    private int numCars = 20; //no more than 100
    private int numCarsGoal = 20;
    private boolean newCarFlag = false;
    private Grid grid = new Grid();
    private ArrayList carArray = new ArrayList();
    private Car carBuffer;
    private int[][] occupancyMatrix = new int[37][37];
    Random rand = new Random();
    
    public CarsControl() //calls the functions to create the occupancy matrix, and spawn the first set of cars
    {
        initializeOccMatrix();
        spawnStartingCars();
    }
    
    public void removeCarFromList(int index) //removes specified car from the arraylist
    {
        carArray.remove(index);
    }
    
    public void reduceNumCarsByOne() //reduces the numcars variable by one
    {
        numCars--;
    }
    
    public void printOM() //only used for debugging
    {
        int i, j;
        System.out.println("Current OM");
        for(i=0; i<37; i++)
        {
            for(j=0; j<37; j++)
            {
                System.out.print(occupancyMatrix[i][j]);
                
            }
            System.out.println();
        }
    }
    
    private void initializeOccMatrix() //set all elements to 0 to begin with
    {
        int i, j;
        
        for(i = 0; i < 37; i++)
            for(j = 0; j < 37; j++)
            {
                occupancyMatrix[i][j] = 0;
            }
    }
    
    private void spawnStartingCars() //spawns the first set of cars
    {
        
        int i, j, sR, sC, dR, dC, dir;
        
        for(i = 0; i < numCars; i++) //create initial cars
        {
            sR = sC = dR = dC = dir = 0;
            while(sR == dR && sC == dC) //while start and destination are the same node, retry
            {
                sR = rand.nextInt(10);
                sC = rand.nextInt(10);
                dR = rand.nextInt(10);
                dC = rand.nextInt(10);
            }
            
            for(j = 0; j <= i; j++) //check to make sure the car being generated is on an unused intersection
            {
                if(j == i) //we check all J elements up to I, if J hasn't failed before reaching I then there are no duplicates of the starting values we are setting
                {
                    carBuffer = new Car(sR, sC, dR, dC); //create the car object
                    carBuffer.setPath(grid.path((10*sR + sC), (10*dR + dC))); //set the path since grid is created in this class and not for each car created
                    //System.out.println("Car: "+i+" new path: "+carBuffer.getPath());
                    carBuffer.getNextIntersection();
                    carBuffer.removeIntersectionFromPath();
                    carBuffer.setOMLocation(4*sR, 4*sC); //save the OM location directly to the car for easy association
                    occupancyMatrix[4*sR][4*sC] = 1; //set location in OM to 1, for being occupied
                    if( sR > carBuffer.getCurrentDest()/10)
                        dir = 0;//north
                    else if(sR < carBuffer.getCurrentDest()/10)
                        dir = 2;//south
                    else if(sC < carBuffer.getCurrentDest()%10)
                        dir = 1;//east
                    else if(sC > carBuffer.getCurrentDest()%10)
                        dir = 3;//west
                    
                    carBuffer.setDirection(dir);
                    
                    carArray.add(carBuffer);
                }
                else if(carBuffer.getStartI() == sR && carBuffer.getStartJ() == sC) //this will check to see if the new car's starting values already exist in our car array
                {
                    i--; //keep trying to fill this element until successful
                    break;
                }
            }
        }
    }
    
    public void checkNumCarsGoal() //checks to see if the goal of numcars is greater than the current value, and creates new cars if needed
    {
        if(numCarsGoal > numCars)
        {
            
            int i, sR, sC, dR, dC, dir;
            boolean spawn = false;
            Car checkBufferAgainst;
            
            while(spawn == false) //create initial cars
            {
                spawn = true;
                sR = sC = dR = dC = dir = 0;
                while(sR == dR && sC == dC) //while start and destination are the same node, retry
                {
                    sR = rand.nextInt(10);
                    sC = rand.nextInt(10);
                    dR = rand.nextInt(10);
                    dC = rand.nextInt(10);
                }
            
                for(i = 0; i < numCars; i++)
                {
                    checkBufferAgainst = (Car)carArray.get(i);
                    if(checkBufferAgainst.getOMI() == 4*sR && checkBufferAgainst.getOMJ() == 4*sC)
                    spawn = false;
                }
            
                if(spawn)
                {
                    carBuffer = new Car(sR, sC, dR, dC); //create the car object
                    carBuffer.setPath(grid.path((10*sR + sC), (10*dR + dC))); //set the path since grid is created in this class and not for each car created
                    
                    carBuffer.getNextIntersection();
                    carBuffer.removeIntersectionFromPath();
                    carBuffer.setOMLocation(4*sR, 4*sC); //save the OM location directly to the car for easy association
                    
                    if( sR > carBuffer.getCurrentDest()/10)
                        dir = 0;//north
                    else if(sR < carBuffer.getCurrentDest()/10)
                        dir = 2;//south
                    else if(sC < carBuffer.getCurrentDest()%10)
                        dir = 1;//east
                    else if(sC > carBuffer.getCurrentDest()%10)
                        dir = 3;//west
                    
                    //This is an attempt to minimize the chance of deadlock, only spawns a car at intersections where no cars are around
                    if( ((4*sR)+1 < 37 && occupancyMatrix[(4*sR)+1][4*sC] == 1) || ((4*sC)+1 < 37 && occupancyMatrix[(4*sR)][4*sC+1] == 1) 
                            || ((4*sR)-1 >= 0 && occupancyMatrix[(4*sR)-1][4*sC] == 1) || ((4*sC)-1 >= 0 && occupancyMatrix[(4*sR)][4*sC-1] == 1) )
                    {
                        spawn = false;
                    }
                    
                    if(spawn == true)
                    {
                        occupancyMatrix[4*sR][4*sC] = 1; //set location in OM to 1, for being occupied
                        carBuffer.setDirection(dir);
                        carArray.add(carBuffer);
                        numCars++;
                        newCarFlag = true;
                        //System.out.println("NEW Car: "+numCars+" new path: "+carBuffer.getPath());
                    }
                }
            }
            
        }
    }
    
    public boolean getNewCarFlag() //returns newcarflag
    {
        return newCarFlag;
    }
    
    public void resetNewCarFlag() //sets newcarflag to false
    {
        newCarFlag = false;
    }
    
    public void setOMForDespawnedCar(int index) //when a car is despawned, need to set the corresponding value in the occupancy matrix to 0. Thereby freeing that space.
    {
        carBuffer = (Car)carArray.get(index);
        occupancyMatrix[carBuffer.getOMI()][carBuffer.getOMJ()] = 0; //make old OM location vacant
    }
    
    public void spawnUserCar(int start, int end) //creates the user's car when they click the button to do so
    {
        int sR, sC, dR, dC, dir;
        
        dir = 0;
        sR = start/10;
        sC = start%10;
        dR = end/10;
        dC = end%10;

        carBuffer = new Car(sR, sC, dR, dC); //create the car object
        carBuffer.setPath(grid.path((10*sR + sC), (10*dR + dC))); //set the path since grid is created in this class and not for each car created

        carBuffer.getNextIntersection();
        carBuffer.removeIntersectionFromPath();
        carBuffer.setOMLocation(4*sR, 4*sC); //save the OM location directly to the car for easy association

        if( sR > carBuffer.getCurrentDest()/10)
            dir = 0;//north
        else if(sR < carBuffer.getCurrentDest()/10)
            dir = 2;//south
        else if(sC < carBuffer.getCurrentDest()%10)
            dir = 1;//east
        else if(sC > carBuffer.getCurrentDest()%10)
            dir = 3;//west


            carBuffer.setDirection(dir);
            occupancyMatrix[4*sR][4*sC] = 1; //set location in OM to 1, for being occupied
            carArray.add(carBuffer);
            numCars++;
            //System.out.println("User car created at: "+ start +" new path: "+carBuffer.getPath());


        
    }
    
    public void spawnNewCar(int index) //creates a new car in the place of the old car, once the old car has reached it's destination and needs to be despawned.
    {
        int i, sR, sC, dR, dC, dir;
        boolean spawn = false;
        Car checkBufferAgainst;
        carBuffer = (Car)carArray.get(index);
        occupancyMatrix[carBuffer.getOMI()][carBuffer.getOMJ()] = 0; //make old OM location vacant
        
        while(spawn == false) //create initial cars
        {
            spawn = true;
            sR = sC = dR = dC = dir = 0;
            while(sR == dR && sC == dC) //while start and destination are the same node, retry
            {
                sR = rand.nextInt(10);
                sC = rand.nextInt(10);
                dR = rand.nextInt(10);
                dC = rand.nextInt(10);
            }
            
            for(i = 0; i < numCars; i++)
            {
                checkBufferAgainst = (Car)carArray.get(i);
                if(checkBufferAgainst.getOMI() == 4*sR && checkBufferAgainst.getOMJ() == 4*sC)
                    spawn = false;
            }
            
            if(spawn)
            {
                carBuffer = new Car(sR, sC, dR, dC); //create the car object
                carBuffer.setPath(grid.path((10*sR + sC), (10*dR + dC))); //set the path since grid is created in this class and not for each car created
                
                carBuffer.getNextIntersection();
                carBuffer.removeIntersectionFromPath();
                carBuffer.setOMLocation(4*sR, 4*sC); //save the OM location directly to the car for easy association
                
                if( sR > carBuffer.getCurrentDest()/10)
                    dir = 0;//north
                else if(sR < carBuffer.getCurrentDest()/10)
                    dir = 2;//south
                else if(sC < carBuffer.getCurrentDest()%10)
                    dir = 1;//east
                else if(sC > carBuffer.getCurrentDest()%10)
                    dir = 3;//west
                
                //This is an attempt to minimize the chance of deadlock, only spawns a car at intersections where no cars are around
                if( ((4*sR)+1 < 37 && occupancyMatrix[(4*sR)+1][4*sC] == 1) || ((4*sC)+1 < 37 && occupancyMatrix[(4*sR)][4*sC+1] == 1) 
                        || ((4*sR)-1 >= 0 && occupancyMatrix[(4*sR)-1][4*sC] == 1) || ((4*sC)-1 >= 0 && occupancyMatrix[(4*sR)][4*sC-1] == 1) )
                {
                    spawn = false;
                }
                
                
                if(spawn == true)
                {
                    carBuffer.setDirection(dir);
                    occupancyMatrix[4*sR][4*sC] = 1; //set location in OM to 1, for being occupied
                    carArray.set(index, carBuffer);
                    //System.out.println("Car: "+index+" new path: "+carBuffer.getPath());
                }
            }
        }
    }
    
    public void setNumCarsGoal(int num) //set's the numcarsgoal, this is effected by the numcars slider
    {
        numCarsGoal = num;
    }
    
    public int getNumCarsGoal() //returns numcarsgoal
    {
        return numCarsGoal;
    }
    
    public Car getCarFromArray(int i) //returns the car object from the arraylist index
    {
        carBuffer = (Car)carArray.get(i);
        return carBuffer;
    }
    
    public int getNumcars() //returns numcars
    {
        return numCars;
    }
    
    public int getOMValue(int i, int j) //returns the value held by the specified element in the occupancy matrix
    {
        return occupancyMatrix[i][j];
    }
    
    public void setOMValue(int i, int j, int val) //sets the value of the element in the specified index of the occupancy matrix, either 0 or 1, 0 for vacant, 1 for occupied
    {
        occupancyMatrix[i][j] = val;
    }
    
}
